package top.kaoshanji.ch03;

import com.google.common.io.Resources;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.w3c.dom.CharacterData;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

/**
 * 代码清单 ch03-02：验证XML文档示例(3.4节、两种机制来实例化对象)
 * 使用构造器和使用工厂方法
 * author kaoshanji
 * ^_^
 * create time 2024/12/7 11:37
 */
public class XMLReadTest {

    private static final Logger logger = LogManager.getLogger(XMLReadTest.class);

    static final String FILE_NAME = "ch03/config.xml";

    public static void main(String[] args) throws ParserConfigurationException, IOException, SAXException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException, InstantiationException {
        logger.info("...代码清单 ch03-02：验证XML文档示例(3.4节、两种机制来实例化对象)...");

        URL url = Resources.getResource(FILE_NAME);

        // 这是解析的固定流程...
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setValidating(true);
        factory.setIgnoringElementContentWhitespace(true);

        DocumentBuilder builder = factory.newDocumentBuilder();

        // 错误解析器
        builder.setErrorHandler(new ErrorHandler() {
            @Override
            public void warning(SAXParseException exception) throws SAXException {
                System.err.println("Warning: " + exception.getMessage());
            }

            @Override
            public void error(SAXParseException exception) throws SAXException {
                System.err.println("Error: " + exception.getMessage());
                System.exit(0);
            }

            @Override
            public void fatalError(SAXParseException exception) throws SAXException {
                System.err.println("Fatal error: " + exception.getMessage());
                System.exit(0);
            }
        });

        Document doc = builder.parse(url.openStream());

        // 处理逻辑的入口...节点的层次代表了方法调用的层次
        Map<String, Object> config = parseConfig(doc.getDocumentElement());
        System.out.println(config);

    }

    private static Map<String, Object> parseConfig(Element e) throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException, InstantiationException {

        var result = new HashMap<String, Object>();
        NodeList children = e.getChildNodes();
        for (int i = 0; i < children.getLength(); i++) {
            var child = (Element)children.item(i);
            String name = child.getAttribute("id");
            Object value = parseObject((Element) child.getFirstChild());
            result.put(name, value);
        }

        return result;
    }

    private static Object parseObject(Element e) throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException, InstantiationException {

        String tagName = e.getTagName();
        if (tagName.equals("factory")) {
            return parseFactory(e);
        } else if (tagName.equals("construct")) {
            return parseConstruct(e);
        } else {
            String childData = ((CharacterData) e.getFirstChild()).getData();
            if (tagName.equals("int")) {
                return Integer.valueOf(childData);
            } else if (tagName.equals("boolean")) {
                return Boolean.valueOf(childData);
            } else {
                return childData;
            }
        }

    }

    private static Object parseFactory(Element e) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {

        String className = e.getAttribute("class");
        String methodName = e.getAttribute("method");
        Object[] args = parseArgs(e.getChildNodes());
        Class<?>[] parameterTypes = getParameterTypes(args);
        Method method = Class.forName(className).getMethod(methodName, parameterTypes);
        return method.invoke(null, args);

    }

    private static Object parseConstruct(Element e) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {

        String className = e.getAttribute("class");
        Object[] args = parseArgs(e.getChildNodes());
        Class<?>[] parameterTypes = getParameterTypes(args);
        Constructor<?> constructor = Class.forName(className).getConstructor(parameterTypes);
        return constructor.newInstance(args);

    }

    private static Object[] parseArgs(NodeList elements) throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException, InstantiationException {

        var result = new Object[elements.getLength()];
        for (int i = 0; i < result.length; i++) {
            result[i] = parseObject((Element) elements.item(i));
        }
        return result;

    }

    private static Map<Class<?>, Class<?>> toPrimitive = Map.of(
            Integer.class, int.class,
            Boolean.class, boolean.class);

    private static Class<?>[] getParameterTypes(Object[] args) {

        var result = new Class<?>[args.length];
        for (int i = 0; i < result.length; i++) {
            Class<?> cl = args[i].getClass();
            result[i] = toPrimitive.get(cl);
            if (result[i] == null) {
                result[i] = cl;
            }
        }
        return result;

    }

}
